//===--- Scope.cpp - Scope Implementation ---------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
//  This file implements semantic analysis for Swift declarations.
//
//===----------------------------------------------------------------------===//

#include "polarphp/llparser/Scope.h"
#include "polarphp/llparser/Parser.h"
#include "llvm/ADT/Twine.h"

namespace polar::llparser {

//===----------------------------------------------------------------------===//
// Scope Implementation
//===----------------------------------------------------------------------===//

static bool isResolvableScope(ScopeKind SK) {
   switch (SK) {
      case ScopeKind::Extension:
      case ScopeKind::EnumBody:
      case ScopeKind::StructBody:
      case ScopeKind::ClassBody:
      case ScopeKind::InterfaceBody:
      case ScopeKind::TopLevel:
      case ScopeKind::InheritanceClause:
         return false;
      case ScopeKind::FunctionBody:
      case ScopeKind::Generics:
      case ScopeKind::Brace:
      case ScopeKind::ForeachVars:
      case ScopeKind::ClosureParams:
      case ScopeKind::CaseVars:
      case ScopeKind::CatchVars:
      case ScopeKind::IfVars:
      case ScopeKind::WhileVars:
         return true;
   }

   llvm_unreachable("Unhandled ScopeKind in switch.");
}

Scope::Scope(Parser *P, ScopeKind SC, bool isInactiveConfigBlock)
   : SI(P->getScopeInfo()),
     HTScope(SI.HT, SI.CurScope ? &SI.CurScope->HTScope : nullptr),
     PrevScope(SI.CurScope),
     PrevResolvableDepth(SI.ResolvableDepth),
     Kind(SC),
     IsInactiveConfigBlock(isInactiveConfigBlock) {
   assert(PrevScope || Kind == ScopeKind::TopLevel);

   if (SI.CurScope) {
      Depth = SI.CurScope->Depth + 1;
      IsInactiveConfigBlock |= SI.CurScope->IsInactiveConfigBlock;
   } else {
      Depth = 0;
   }
   SI.CurScope = this;
   if (!isResolvableScope(Kind))
      SI.ResolvableDepth = Depth + 1;
}

Scope::Scope(Parser *P, SavedScope &&SS) :
   SI(P->getScopeInfo()),
   HTScope(std::move(SS.HTDetachedScope)),
   PrevScope(SI.CurScope),
   PrevResolvableDepth(SI.ResolvableDepth),
   Depth(SS.Depth),
   Kind(SS.Kind),
   IsInactiveConfigBlock(SS.IsInactiveConfigBlock) {

   SI.CurScope = this;
   if (!isResolvableScope(Kind))
      SI.ResolvableDepth = Depth + 1;
}

bool Scope::isResolvable() const {
   return isResolvableScope(Kind);
}

//===----------------------------------------------------------------------===//
// ScopeInfo Implementation
//===----------------------------------------------------------------------===//

/// checkValidOverload - Check whether it is ok for D1 and D2 to be declared at
/// the same scope.  This check is a transitive relationship, so if "D1 is a
/// valid overload of D2" and "D2 is a valid overload of D3" then we know that
/// D1/D3 are valid overloads and we don't have to check all permutations.
static bool checkValidOverload(const ValueDecl *D1, const ValueDecl *D2,
                               Parser &P) {
   // Currently, there is no restriction on overloading.
   return false;
}


/// addToScope - Register the specified decl as being in the current lexical
/// scope.
void ScopeInfo::addToScope(ValueDecl *D, Parser &TheParser,
                           bool diagnoseRedefinitions) {
   if (!CurScope->isResolvable())
      return;

   assert(CurScope->getDepth() >= ResolvableDepth &&
          "inserting names into a non-resolvable scope");

   // If we have a shadowed variable definition, check to see if we have a
   // redefinition: two definitions in the same scope with the same name.
   ScopedHTTy::iterator EntryI = HT.begin(CurScope->HTScope, D->getFullName());

   // A redefinition is a hit in the scoped table at the same depth.
   if (EntryI != HT.end() && EntryI->first == CurScope->getDepth()) {
      ValueDecl *PrevDecl = EntryI->second;

      // If this is in a resolvable scope, diagnose redefinitions.  Later
      // phases will handle scopes like module-scope, etc.
      if (CurScope->getDepth() >= ResolvableDepth) {
         if (diagnoseRedefinitions) {
            return TheParser.diagnoseRedefinition(PrevDecl, D);
         }
         return;
      }

      // If this is at top-level scope, validate that the members of the overload
      // set all agree.

      // Check to see if D and PrevDecl are valid in the same overload set.
      if (checkValidOverload(D, PrevDecl, TheParser))
         return;

      // Note: we don't check whether all of the elements of the overload set have
      // different argument types.  This is checked later.
   }

   HT.insertIntoScope(CurScope->HTScope,
                      D->getFullName(),
                      std::make_pair(CurScope->getDepth(), D));
}


// Disable the "for use only in debugger" warning.
#ifdef POLAR_CC_MSVC
#pragma warning(push)
#pragma warning(disable : 4996)
#endif

void ScopeInfo::dump() const {
#ifndef NDEBUG
   // Dump out the current list of scopes.
   if (!CurScope->isResolvable())
      return;

   assert(CurScope->getDepth() >= ResolvableDepth &&
          "Attempting to dump a non-resolvable scope?!");

   llvm::dbgs() << "--- Dumping ScopeInfo ---\n";
   std::function<void(decltype(HT)::DebugVisitValueTy)> func =
      [&](const decltype(HT)::DebugVisitValueTy &iter) -> void {
         llvm::dbgs() << "DeclName: " << iter->getKey() << "\n"
                      << "KeyScopeID: " << iter->getValue().first << "\n"
                      << "Decl: ";
         iter->getValue().second->dumpRef(llvm::dbgs());
         llvm::dbgs() << "\n";
      };
   HT.debugVisit(std::move(func));
   llvm::dbgs() << "\n";
#endif
}

#ifdef POLAR_CC_MSVC
#pragma warning(pop)
#endif

} // polar::llparser
